using System;
using System.Workflow.ComponentModel;
using System.ComponentModel;
using System.IO.Packaging;
using System.Collections.Generic;
using System.Xml;
using System.IO;

namespace OWA.Activities
{
	public class InspectWMLPackageActivity
        : AbstractPackageActivity
	{
        static readonly string XMLNS_WML = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
        static readonly string RT_Comments = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments";

        bool _removeComments = true;
        bool _removeReviewingData;

        [Category("OWA")]
        [Description("Indicates whether to remove reviewing data from the package.")]
        [DefaultValue(true)]
        public bool RemoveReviewingData
        {
            get { return _removeReviewingData; }
            set { _removeReviewingData = value; }
        }

        [Category("OWA")]
        [Description("Indicates whether to remove comments from the package.")]
        [DefaultValue(true)]
        public bool RemoveComments
        {
            get { return _removeComments; }
            set { _removeComments = value; }
        }

        protected override ActivityExecutionStatus Execute(
            ActivityExecutionContext executionContext)
        {
            Package package = GetPackage();
            PackagePart mainPart = GetMainPart(package);
            XmlDocument document = LoadDocument(mainPart);
            if (_removeComments)
            {
                RemoveCommentParts(mainPart);
                RemoveCommentMarkup(document);
            }
            if (_removeReviewingData)
            {
                RemoveReviewingInternal(document);
            }
            SaveDocument(document, mainPart);
            return ActivityExecutionStatus.Closed;
        }

        private XmlDocument LoadDocument(PackagePart mainPart)
        {
            try
            {
                XmlDocument document = new XmlDocument();
                using (Stream stream = mainPart.GetStream(
                    FileMode.Open, FileAccess.Read))
                {
                    document.Load(stream);
                }
                return document;
            }
            catch (XmlException e)
            {
                throw new OWAException("Unexpected XML error while remoing comment markup.", e);
            }
        }

        public void SaveDocument(XmlDocument document, PackagePart mainPart)
        {
            try
            {
                using (Stream stream = mainPart.GetStream(
                    FileMode.Open, FileAccess.ReadWrite))
                {
                    document.Save(stream);
                }
            }
            catch (XmlException e)
            {
                throw new OWAException("Unexpected XML error while remoing comment markup.", e);
            }
        }

        void RemoveCommentParts(PackagePart mainPart)
        {
            List<string> relationshipIDs = new List<string>();
            List<Uri> commentsParts = new List<Uri>();

            foreach (PackageRelationship relationship in
                mainPart.GetRelationshipsByType(RT_Comments))
            {
                relationshipIDs.Add(relationship.Id);
                commentsParts.Add(
                    PackUriHelper.ResolvePartUri(
                        relationship.SourceUri, relationship.TargetUri));
            }
            foreach (string id in relationshipIDs)
            {
                mainPart.DeleteRelationship(id);
            }
            foreach (Uri uri in commentsParts)
            {
                mainPart.Package.DeletePart(uri);
            }
        }

        void RemoveCommentMarkup(XmlDocument document)
        {
            string[] xpathQueries = new string[]
            {
                "//w:commentRangeStart",
                "//w:commentReference",
                "//w:commentRangeEnd"
            };
            RemoveSelectedNodes(document, xpathQueries, false);
        }

        void RemoveReviewingInternal(XmlDocument document)
        {
            string[] xpathQueries = new string[]
            {
                "//w:rPrChange",
                "//w:pPrChange",
                "//w:tblPrChange",
                "//w:trPrChange",
                "//w:tcPrChange",
                "//w:moveFrom",
                "//w:moveFromRangeStart",
                "//w:moveFromRangeEnd",
                "//w:moveToRangeStart",
                "//w:moveToRangeEnd",
                "//w:del",
                "//w:delText"
            };
            string[] maintainChildrenQueries = new string[]{
                "//w:ins", "//w:moveTo"};
            RemoveSelectedNodes(document, xpathQueries, false);
            RemoveSelectedNodes(document, maintainChildrenQueries, true);
        }

        void RemoveSelectedNodes(
            XmlDocument document, string[] xpathQueries, bool maintainChildren)
        {
            List<XmlNode> nodesToDelete = new List<XmlNode>();
            XmlNamespaceManager mgr = new XmlNamespaceManager(document.NameTable);
            mgr.AddNamespace("w", XMLNS_WML);
            foreach (string xpath in xpathQueries)
            {
                foreach (XmlNode node in document.SelectNodes(
                    xpath, mgr))
                {
                    nodesToDelete.Add(node);
                }
            }
            foreach (XmlNode node in nodesToDelete)
            {
                if (maintainChildren)
                {
                    foreach (XmlNode childNode in node.ChildNodes)
                    {
                        node.ParentNode.InsertAfter(childNode, node);
                    }
                }
                node.ParentNode.RemoveChild(node);
            }
        }
	}
}
